﻿using SchedulerLite.BLL;
using SchedulerLite.Model.Entity;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SchedulerLite.Common.Quartz;
using System.Net.Http;
using SchedulerLite.Common;

namespace SchedulerLite.Service
{
    class TimedTaskManager
    {
        private int _loadIntervalCount = 0;
        private int _loadIntervalSeconds = 0;

        private Dictionary<CronExpression, List<long>> taskDict;

        public TimedTaskManager()
        {
            _loadIntervalCount = _loadIntervalSeconds = Config.LoadTimedTaskIntervalSeconds;
            taskDict = new Dictionary<CronExpression, List<long>>();
        }

        public void LoadTask(DateTime dateTime)
        {
            if (_loadIntervalSeconds > 0)
            {
                if (_loadIntervalCount >= _loadIntervalSeconds)
                {
                    _loadIntervalCount = 0;
                }
                if (_loadIntervalCount == 0)
                {
                    var taskGroup = B_TimedTask.SelectReadyList();
                    lock (this)
                    {
                        taskDict.Clear();
                        foreach (var item in taskGroup)
                        {
                            taskDict.Add(new CronExpression(item.Key), item.Value);
                        }
                    }
                }
                _loadIntervalCount++;
            }
        }

        public void TriggerTask(DateTime dateTime)
        {
            List<long> taskIds = new List<long>();
            lock (this)
            {
                foreach (var task in taskDict)
                {
                    if (task.Key.IsSatisfiedBy(dateTime.ToUniversalTime()))
                    {
                        taskIds.AddRange(task.Value);
                    }
                }
            }
            if (taskIds.Count > 0)
            {
                var taskList = B_TimedTask.SelectByIds(taskIds);
                if (taskList.Count > 0)
                {
                    taskList.AsParallel().ForAll(Run);
                }
            }
        }

        private void Run(TimedTask task)
        {
            HttpClient hc = new HttpClient();
            hc.Timeout = task.TimeoutSeconds == 0 ? TimeSpan.FromSeconds(15) : TimeSpan.FromSeconds(task.TimeoutSeconds);
            Task<HttpResponseMessage> requestTask;
            if (task.Method.Equals("GET", StringComparison.OrdinalIgnoreCase))
            {
                requestTask = hc.GetAsync(task.Url);
            }
            else
            {
                StringContent stringContent = new StringContent(task.PostData, Encoding.UTF8);
                requestTask = hc.PostAsync(task.Url, stringContent);
            }
            task.LastExecTime = DateTime.Now;

            requestTask.ContinueWith(reqTask =>
            {
                var log = new ExecuteLog()
                {
                    PostData = task.PostData,
                    TaskId = task.Id,
                    TaskName = task.Name,
                    TaskUrl = task.Url,
                    TaskType = 1,
                    TaskMethod = task.Method
                };
                if (reqTask.IsFaulted)
                {
                    if (hc != null) hc.Dispose();
                    log.Status = 2;
                    log.Message = reqTask.Exception.GetBaseException().StackTrace;
                    B_ExecuteLog.Add(log);
                    task.LastStatus = 2;
                    B_TimedTask.ExecuteOne(task);
                }
                else if (reqTask.IsCanceled)
                {
                    if (hc != null) hc.Dispose();
                    log.Status = 3;
                    log.Message = "timeout";
                    B_ExecuteLog.Add(log);
                    task.LastStatus = 3;
                    B_TimedTask.ExecuteOne(task);
                }
                else
                {
                    reqTask.Result.Content.ReadAsStringAsync().ContinueWith(readTask =>
                    {
                        if (hc != null) hc.Dispose();
                        if (readTask.Result == task.SuccessFlag)
                        {
                            log.Status = 1;
                            log.Message = "success";
                            B_ExecuteLog.Add(log);
                            task.LastStatus = 1;
                            B_TimedTask.ExecuteOne(task);
                        }
                        else
                        {
                            log.Status = 2;
                            log.Message = readTask.Result;
                            B_ExecuteLog.Add(log);
                            task.LastStatus = 2;
                            B_TimedTask.ExecuteOne(task);
                        }
                    });
                }
            });
        }
    }
}
